﻿using System;
using System.Data;
using System.Data.SqlServerCe;
using System.Diagnostics;
using Outlook = Microsoft.Office.Interop.Outlook;

namespace Investafinance.Sync.Shell
{
    /// <summary>
    ///     Обалочка для Synchronizer тут перехватываются все события синхронизации   
    /// </summary>
    public class ShellSynchronizer <ItemType> : SyncObject where ItemType : class
    {
        #region Свойства
            // строка конекта к бызе данных
            private string scon;                    // = @"Data Source=C:\Meta.sdf";
            //Название таблиц
            private string nameTable;               // = "Sync_Collaboratori";
            private string nameFolder;              // = "Collaboratore";
            private string typeMessge;              // = "IPM.Contact.Collaboratore";
           // private string nameTableMeta;
            public ShellFieldItem<ItemType> CollectionField;
        #endregion

        #region Конструктор
            /// <summary>
            /// Конструктор
            /// </summary>
            /// <param name="nameTableDB">Название синхронизируемой таблици (должно совподать с названием листа SharePoint) !=Null && !=""</param>
            /// <param name="nameFolderOutlook">Название папки Outlook !=Null </param>
            /// <param name="connectString">Строка конекта к БД !=Null && !=""</param>
            /// <param name="MessageClass">Класс принимаемых сообщений например "IPM.Contact.Collaboratore" !=Null && !=""</param>
            /// <param name="CollectionFields">Обект типа ShellFieldItem </param>
            public ShellSynchronizer(string nameTableDB, 
                                     string nameFolderOutlook, 
                                     string connectString, 
                                     ShellFieldItem<ItemType> CollectionFields)

            {
                Debug.Assert(connectString != null, "ShellSynchronizer.cs конструктор connectString != null");
                Debug.Assert(connectString != "", "ShellSynchronizer.cs конструктор connectString != ''");
                Debug.Assert(nameTableDB != null, "ShellSynchronizer.cs конструктор nameTableDB != null");
                Debug.Assert(nameTableDB != "", "ShellSynchronizer.cs конструктор nameTableDB != ''");
                Debug.Assert(nameFolderOutlook != null, "ShellSynchronizer.cs конструктор nameFolderOutlook != null");
            

                this.scon = connectString;
                this.nameTable = nameTableDB;
                this.nameFolder = nameFolderOutlook;
                this.CollectionField = CollectionFields;
                associateMap = CollectionFields.associateMap;
            }
        #endregion

        #region Инициализация (при старте синхронизации)
            /// <summary>
            /// Инициализация (при старте синхронизации)
            /// </summary>
            /// <param name="Folder">Указатель на папку в Outlook !=null</param>
            /// <param name="listNameVar">Название листа на SharePoint !=null !=""</param>
            public void InitializeSync(ShellFolderSync<ItemType> WorkFolder,  string listNameVar, Credential user)
            {
                Debug.Assert(WorkFolder != null, "ShellSynchronizer.cs конструктор WorkFolder != null");
                Debug.Assert(listNameVar != null, "ShellSynchronizer.cs конструктор listNameVar != null");
                Debug.Assert(listNameVar != "", "ShellSynchronizer.cs конструктор listNameVar != ''");


                SqlCeConnection conn = new SqlCeConnection(scon);
                try
                {
                    conn.Open();
                    SqlCeCommand cmd = new SqlCeCommand("select MessageClass,ListName from Folders where EntryID='" + WorkFolder.MapiFolder.EntryID + "';", conn);
                    SqlCeDataReader dr = cmd.ExecuteReader();
                    if (dr.Read())
                    {
                        this.typeMessge = dr["MessageClass"].ToString();
                        listNameVar = dr["ListName"].ToString();
                    }
                }
                catch (Exception ex)
                {
                    Debug.Assert(false, "ShellSynchronizer.cs InitializeSync this.typeMessge  == null " + ex.Message);
                }
                finally
                {
                    conn.Close();
                }

                base.polaKEY = new string[1, 2];
                polaKEY[0, 0] = "ID";
                polaKEY[0, 1] = "string";
                this.UpdateFolder();

                string collectionFieldInSharepoint = ",";
                associateMap = WorkFolder.associateMap; //ShellFolderSync<ItemType>.associateMap;

                for (int i = 0; i < Convert.ToInt32(associateMap.Length / 4); i++)
                {
                    collectionFieldInSharepoint += associateMap[i, 0] + ",";
                }

                Sync(WorkFolder.MapiFolder, collectionFieldInSharepoint, listNameVar, user);

            }

            private string[,] associateMap;
            private SyncSQLServer<ItemType> sync;

            private void Sync(Outlook.MAPIFolder Folder, string collectionFieldInSharepoint, string listNameVar, Credential user)
            {
                sync = new SyncSQLServer<ItemType>(scon);
                sync.EventCopyTable += EventCopyTable;
                sync.EventCreateUpdate += EventCreateUpdate;
                sync.EventDoIzmeneniaClientaKolisy_1 += EventDoIzmeneniaClientaKolisy_1;
                sync.EventDoIzmeneniaClientaKolisy_3 += EventDoIzmeneniaClientaKolisy_3;
                sync.EventDoIzmeneniaServerKolisy_3 += EventDoIzmeneniaServerKolisy_3;
                sync.EventEndKolizii_1 += EventEndKolizii_1;
                sync.EventGetNewFild += EventGetNewFild;
                sync.EventLoadUpdate += EventLoadUpdate;
                sync.EventOutlookUpdate += EventOutlookUpdate;
                sync.EventPosleIzmeneniaClientaKolisy_1 += EventPosleIzmeneniaClientaKolisy_1;
                sync.EventPosleIzmeneniaClientaKolisy_2 += EventPosleIzmeneniaClientaKolisy_2;
                sync.EventPosleIzmeneniaServerKolisy_2 += EventPosleIzmeneniaServerKolisy_2;
                sync.EventRemuveFild += EventRemuveFild;
                sync.EventStartKolisy_1 += EventStartKolisy_1;
                sync.EventStartKolisy_2 += EventStartKolisy_2;
                sync.EventStartKolisy_3 += EventStartKolisy_3;
                sync.EventProcent += Procent;
                sync.EventUpdateIDInOutlook += EventUpdateIDInOutlook;

                p = new Investafinance.Sync.Forms.PercentageSync();
                p.Show();
                sync.UpdateTable(Folder, (SyncObject)this, collectionFieldInSharepoint,  listNameVar,  user);
                p.Close();
            }
        #endregion

        #region обновления полей (при первой синхронизации не известен набор  полей поэтому перед началом обновляем in sinhronaizer)
            public void UpdateFolder()
            {
                base.table = "Sync_" + nameTable;
                SqlCeConnection conn = new SqlCeConnection(scon);
                try
                {
                    //открываем подключение к бызе
                    conn.Open();
                    SqlCeCommand cmd;

                    string s = "select count(Name) from MetaData_" + nameTable;
                    cmd = new SqlCeCommand(s, conn);
                    SqlCeDataReader dr = cmd.ExecuteReader();
                    
                    if (dr.Read())
                    {
                        //-1 потому что без ID
                        base.pola = new string[Convert.ToInt32(dr[0]), 2];
                        dr.Close();
                        dr.Dispose();
                        cmd = new SqlCeCommand("select Name,Type from MetaData_" + nameTable, conn);
                        dr = cmd.ExecuteReader();
                        int i = 0;
                        while (dr.Read())
                        {
                            if (dr["Name"].ToString() != "ID" || dr["Name"].ToString() != "owshiddenversion")
                            {
                                pola[i, 0] = dr["Name"].ToString();

                                switch (dr["Type"].ToString())
                                {
                                    case "Integer":
                                        pola[i, 1] = "int";
                                        break;
                                    case "Number":
                                        pola[i, 1] = "int";
                                        break;
                                    default:
                                        pola[i, 1] = "string";
                                        break;
                                }
                            }
                            else
                            {
                                pola[i, 0] = "owshiddenversion";
                                pola[i, 1] = "string";
                            }
                            i++;
                        }
                        dr.Close();
                        dr.Dispose();
                    }
                }
                catch (Exception err)
                {
                    if  ( err.Message.IndexOf( "MetaData_" + nameTable ) == -1 )
                    {
                        Debug.Assert(false, "ShellSynchronizer.cs UpdateFolder()" + "\n" + err.Message + "\n -->" + err.Source + "\n"); 
                    }
                }
                finally
                {
                    //закрываем подключение к базе 
                    conn.Close();
                    conn.Dispose();
                    conn = null;
                }
            }
        #endregion

        #region события вставки в клиента и EventUpdateIDInOutlook
            //вставка в клиента .. также вставляем в таблицу access
            private void InsertCollaboratori(DataRow[] RowIn, Outlook.MAPIFolder Folder)
            {
                Essence<ItemType> collaboratori =
                        new Essence<ItemType>(nameTable, nameFolder, scon, null, typeMessge, CollectionField, associateMap);
                foreach (DataRow drow in RowIn)
                {
                    //Будим в цикле создавать класс вакансии с инициализировать его полями
                    collaboratori.New(Folder);
                    for (int i = 0; i < CollectionField.GetCountField(); i++)
                    {
                        string sName = CollectionField.GetNameField(i);
                        collaboratori[sName, ShellFieldItem<ItemType>.GetAssociateName(sName,associateMap)] = drow[sName].ToString();
                    }
                    collaboratori.Save();
                }
            }

            private void EventUpdateIDInOutlook(Object sender, UpdateIDInOutlookEventArgs e)
            {
                Essence<ItemType> collaboratori =
                       new Essence<ItemType>(nameTable, nameFolder, scon, null, typeMessge, CollectionField, associateMap);
                collaboratori.Load(e.CurrentID);
                collaboratori["ID", "Mileage"] = e.ID;
                collaboratori.SaveInOutlook();
            }
        #endregion

        #region всё что связано с процентами
            Investafinance.Sync.Forms.PercentageSync p;

            private void Procent(Object sender, ProcentEventArgs e)
            {
                p.Text = "Синхронизация  "+ e.Procent.ToString() + " %";
                p.progressBar.Value = e.Procent;
            }

        #endregion

        #region 3 События возникает до обновление записей которые у клиента не изменялись но на сервере изменились
        Essence<ItemType> collaboratori;    
            
            //1 создание обекта
            private void EventCreateUpdate(Object sender, EventArgs e)
            {
                collaboratori =
                   new Essence<ItemType>(nameTable, nameFolder, scon, null, typeMessge, CollectionField, associateMap);
            }

            //2 загрузка
            private void EventLoadUpdate(Object sender, EventLoadUpdateEventArgs e)
            {
                collaboratori.Load(e.Drow["ID"].ToString());
            }

            //3 обновление данных в  oulook
            private void EventOutlookUpdate(Object sender, EventOutlookUpdateEventArgs e)
            {
                //запишем теперь изменения в outlook
                for (int i = 0; i < CollectionField.GetCountField(); i++)
                {
                    string sName = CollectionField.GetNameField(i);
                    collaboratori[sName, ShellFieldItem<ItemType>.GetAssociateName(sName,associateMap)] = e.Drow[sName].ToString().Trim();
                }
                collaboratori.SaveInOutlook();
                collaboratori.CloseNONSaveInOutlook();
            }
        #endregion

        #region  Колизия типа изменений ->Внесити свои изменения, 4 делегатa
            //1 Событие до выбора варианта разрешения кализии
            private void EventStartKolisy_1(Object sender, EventStartKolisy_1_EventArgs e)
            {
                //-вопервых нужно показать окно вакансии
                collaboratori =
                   new Essence<ItemType>(nameTable, nameFolder, scon, null, typeMessge, CollectionField, associateMap);

                if (collaboratori.Load(e.Dr["ID"].ToString()))
                {
                    collaboratori.Viev();
                }
            }

            //2 Событие до изменения на стороне клиента (в случае нажатия НЕТ)
            private void EventDoIzmeneniaClientaKolisy_1(Object sender, EventArgs e)
            {
                collaboratori.CloseNONSaveInOutlook();
            }

            //3 Событие до изменения на стороне клиента (в случае нажатия Да)
            private void EventPosleIzmeneniaClientaKolisy_1(Object sender, EventPosleIzmeneniaClientaKolisy_1_EventArgs e)
            {
                //теперь запишем изменения в outlook
                for (int i = 0; i < CollectionField.GetCountField(); i++)
                {
                    string sName = CollectionField.GetNameField(i);
                    collaboratori[sName, ShellFieldItem<ItemType>.GetAssociateName(sName,associateMap)] = e.Kolisi[sName].ToString().Trim();
                }
                collaboratori.Viev();
                collaboratori.SaveInOutlook();
            }

            //4 Сбытие после выбора варианта разрешения колизии выполняется независимо от выбора
            private void EventEndKolizii_1(Object sender, EventArgs e)
            {
                collaboratori.CloseNONSaveInOutlook();
            }
        #endregion

        #region Колизия Удаление вакансии Если удалена было на сервере до последннего изменения то возникает колизия предлогаем пользователю выбрать внести свои  изменения или оставить изменения которые на сервере
            //1 Событие до выбора варианта разрешения кализии
            private void EventStartKolisy_2(Object sender, EventStartKolisy_2_EventArgs e)
            {
                //-вопервых нужно показать окно вакансии
                collaboratori =
                  new Essence<ItemType>(nameTable, nameFolder, scon, null, typeMessge, CollectionField, associateMap);

                if (collaboratori.LoadItemNonOutlook(e.FolderVacancy, e.Dr["ID"].ToString()))
                {
                    collaboratori.Viev();
                }
            }

            //2 Событие после изменения на стороне Сервера 
            private void EventPosleIzmeneniaServerKolisy_2(Object sender, EventPosleIzmeneniaServerKolisy_2_EventArgs e)
            {
                collaboratori.CloseNONSaveInOutlook();
            }

            //3 Событие после изменения на стороне клиента 
            private void EventPosleIzmeneniaClientaKolisy_2(Object sender, EventPosleIzmeneniaClientaKolisy_2_EventArgs e)
            {
                //-Записываем новые данные В клиента
                InsertCollaboratori(e.Kolisi, e.FolderVacancy);
                collaboratori.CloseNONSaveInOutlook();
            }
        #endregion

        #region Кализия Другой пользователь удалил вакансию после последней синхронизации.\n Создать на сервере Вакансию?"
            //1 Событие до выбора варианта разрешения кализии
            private void EventStartKolisy_3(Object sender, EventStartKolisy_3_EventArgs e)
            {
                //-вопервых нужно показать окно вакансии
                collaboratori =
                   new Essence<ItemType>(nameTable, nameFolder, scon, null, typeMessge, CollectionField, associateMap);

                if (collaboratori.Load(e.Dr["ID"].ToString()))
                {
                    collaboratori.Viev();
                }
            }

            //2 Событие до изменения на стороне cервера 
            private void EventDoIzmeneniaServerKolisy_3(Object sender, EventArgs e)
            {
                collaboratori.CloseNONSaveInOutlook();
            }

            //3 Событие до изменения на стороне клиента
            private void EventDoIzmeneniaClientaKolisy_3(Object sender, EventArgs e)
            {
                collaboratori.RemuveInOutlook();
            }
        #endregion

        #region Ещё 3 события
            //Событие до удаления записи из таблици клиента (По одной записи)
            private void EventRemuveFild(Object sender, EventRemuveFildEventArgs e)
            {
                ((Outlook.ContactItem)ShellFolderSync<ItemType>.localApplication.Session.GetItemFromID(e.Entry, e.Srore)).Delete();
            }

            //Вслучае первова запуска синхронизации таблици возникает это событие 
            //мы должны тупо скопировать тублицу с сервера
            private void EventCopyTable(Object sender, EventCopyTableEventArgs e)
            {
                InsertCollaboratori(e.Dr, e.FolderVacancy);
            }

            //Вот такое вот событие 
            //возмём все записи которые у клиента не обновлял  и не удалял ,and не те с котрыми клиент 
            //ничего не делал но уже скачал с сервера
            private void EventGetNewFild(Object sender, EventGetNewFildEventArgs e)
            {
                InsertCollaboratori(e.Insering, e.FolderVacancy);
            }
        #endregion
    }
}
